<template>
  <span v-if="props.showText">{{ message }}</span>
  <el-select
    v-else
    v-model="currValue"
    clearable
    collapse-tags
    class="remote-distinct"
    popper-class="remote-distinct-box"
    :disabled="inputDisabled"
    :placeholder="calcPlaceholder"
    :filterable="filterable"
    :filter-method="filterMethod"
    :multiple="multiple"
    @compositionstart="compositionStart"
    @compositionend="compositionEnd"
    :popper-append-to-body="true"
    @change="handleChange"
    @clear="handleClear"
  >
    <template #empty>
      <span class="empty">{{ loading ? '数据加载中...' : tipText }}</span>
    </template>
    <span v-if="options.length === 0 || loading" class="empty">{{ loading ? '数据加载中...' : tipText }}</span>
    <span v-if="options.length > 0">
      <el-option
        v-for="item in options"
        :key="item.id"
        :label="(item.name || '').length > 30 ? ((item.name || '').substring(0, 30) + '...') : (item.name || '')"
        :title="(item.name || '').length > 30 ? (item.name || '') : ''"
        :value="item.id"
      ></el-option>
    </span>
    <div v-if="!loadEnd" style="width: 100%;text-align: center;padding-top: 5px">
      <el-button
        :disabled="loading"
        @click="loadPage"
        type="primary"
        link
      >{{ loading ? '正在加载中...' : '加载更多' }}</el-button>
    </div>
  </el-select>
</template>

<script setup>
import { ref, watch, onMounted, computed, onUnmounted, inject, nextTick } from 'vue';
import { useDisabled } from 'element-plus';
import api from '@/http/api';
import { getDataFieldList } from "@/utils/codeless";
import tool from "@/utils/tool";

const emit = defineEmits(['change']);
const props = defineProps({
  prop: String,
  dataModelId: String,
  distinctName: {
    type: String,
    default: 'name'
  },
  filterName: {
    type: String,
    default: 'name'
  },
  filterable: {
    type: Boolean,
    default: true
  },
  multiple: {
    type: Boolean,
    default: false
  },
  placeholder: {
    type: String,
    default: '--请选择--'
  },
  showText: Boolean
});
const formData = inject('formData');

const initializing = ref(false);
const currValue = ref();
const loading = ref(false);
const idProp = ref(props.distinctName);
const nameProp = ref(props.distinctName);
const tipText = ref('请输入内容搜索');
const timer = ref(0);
const options = ref([]);
const message = ref('');
const inputDisabled = useDisabled();
const pageNumber = ref(1);
const pageSize = ref(50);
const loadEnd = ref(false);
const query = ref({});

const calcPlaceholder = computed(() => {
  return inputDisabled.value ? ' ' : props.placeholder;
});

const handleChange = () => {
  if (props.multiple) {
    formData.value[props.prop] = currValue.value ? currValue.value.join(',') : '';
  } else {
    formData.value[props.prop] = currValue.value || '';
  }
  initializing.value = true;
  oldValue.value = '';
  emit('change');
  nextTick(() => {
    initializing.value = false;
  });
};

const handleClear = () => {
  initializing.value = true;
  options.value = [];
  oldValue.value = '';
  formData.value[props.prop] = '';
  emit('change');
  nextTick(() => {
    initializing.value = false;
  });
};

const filterValue = ref('');
const isComposing = ref(false);
const compositionStart = () => {
  isComposing.value = true;
};

const compositionEnd = () => {
  isComposing.value = false;
  filterMethod(filterValue.value);
};

const oldValue = ref('');
const filterMethod = (value) => {
  filterValue.value = value;
  if (isComposing.value) return;
  if (initializing.value) return;
  if (oldValue.value === '' && options.value.length !== 0 && !value) return;

  pageNumber.value = 1;
  options.value = [];
  loadEnd.value = false;
  let params = {};
  params['filterValue'] = value;
  oldValue.value = value;
  loadData(params);
};

const loadPage = () => {
  let params = {};
  params[props.filterName] = oldValue.value;
  loadData(params);
};

const loadData = (params) => {
  if (loading.value) {
    if (timer.value) {
      clearTimeout(timer.value);
    }
    timer.value = setTimeout(() => loadData(params), 500);
  } else {
    if (timer.value) {
      clearTimeout(timer.value);
    }
    timer.value = null;
    doLoadData(params);
  }
};

const doLoadData = async(params) => {
  loading.value = true;
  params = Object.assign({
    pageSize: pageSize.value,
    pageNumber: pageNumber.value,
    distinctName: props.distinctName,
    filterName: props.filterName
  }, params);
  let url = '/codeless/data-model/' + props.dataModelId + '/distinct';
  let res = await api.post(url, params);
  if (res.data && res.data.length > 0) {
    if (res.data.length < pageSize.value) {
      loadEnd.value = true;
    }

    if (pageNumber.value === 1) {
      let arr = [];
      res.data.forEach((item) => {
        if ((item[idProp.value] || '') !== '') {
          arr.push({ id: item[idProp.value], name: item[nameProp.value] })
        }
      });

      options.value = arr;
      calcValue();
    } else {
      res.data.forEach((item) => {
        if ((item[idProp.value] || '') !== '') {
          options.value.push({ id: item[idProp.value], name: item[nameProp.value] })
        }
      });
    }
  } else {
    loadEnd.value = true;
    if (options.value.length === 0) {
      tipText.value = '暂无数据';
    }
  }

  pageNumber.value++;
  loading.value = false;
  timer.value = null;
};

const calcValue = () => {
  if (options.value == null || options.value.length <= 0) {
    message.value = '';
  } else {
    let currOpt = options.value.find(item => item.id === formData.value[props.prop]);
    if (currOpt) {
      message.value = currOpt[props.nameProp];
    } else {
      message.value = '';
    }
  }
};

const loadDataField = async() => {
  if (!props.dataModelId) return;

  const list = await getDataFieldList(props.dataModelId);
  list.forEach(field => {
    if (field.id === props.idProp) {
      idProp.value = field.fieldName;
    }
    if (field.id === props.nameProp) {
      nameProp.value = field.fieldName;
    }
  });
};

const loadInitData = async() => {
  if (!formData.value[props.prop]) return;

  const params = {};
  params[idProp.value] = formData.value[props.prop];
  let url = '/codeless/data-model/' + props.dataModelId + '/distinct';
  let res = await api.post(url, params);
  if (res.data && res.data.length > 0) {
    const data = res.data[0];
    const find = options.value.find(option => option.id === data[idProp.value]);
    if (!find) {
      message.value = data[nameProp.value];
      options.value.push({ id: data[idProp.value], name: data[nameProp.value] })
    }
  }
};

const init = async(data) => {
  if (initializing.value) return;
  initializing.value = true;

  options.value = [];
  let option = data.option || {};
  if (option.id) {
    options.value.unshift(option);
    message.value = option.name;
  }
  await loadDataField();
  await loadInitData();
  initializing.value = false
};

watch(() => tool.deepClone(props), (data, oldData) => {
  if (props.option && props.option.id) {
    let find = options.value.find(item => item.id === props.option.id);
    if (!find) {
      options.value.unshift(props.option);
    }
  }

  if (JSON.stringify(data) !== JSON.stringify(oldData)) {
    init(data);
  }
}, {deep: true});

watch(() => props.dataModelId, () => {
  options.value = [];
});

watch(() => formData.value, () => {
  if (formData.value[props.prop]) {
    const find = options.value.find(item => item.id === formData.value[props.prop]);
    if (!find && formData.value[props.prop + "_name"]) {
      const option = {};
      option['id'] = formData.value[props.prop] || '';
      option['name'] = formData.value[props.prop + "_name"] || '';
      options.value.unshift(option);
    }
  }

  if (props.queryList && props.queryList.length > 0) {
    let queryChange = false;
    const queryValue = {};
    props.queryList.forEach(item => {
      queryValue[item.filterFieldId] = formData.value[item.dataFieldName] || '--blank-value--';
      if (queryValue[item.filterFieldId] !== query.value[item.filterFieldId]) {
        queryChange = true;
      }
    });

    if (queryChange) {
      options.value = [];
      query.value = queryValue;
    }
  }
}, {immediate: true, deep: 1});

onMounted(() => {
  options.value = [];
  init(props);
});

onUnmounted(() => {
  clearTimeout(timer.value);
  options.value = [];
  message.value = '';
});
</script>

<style>
.remote-select-box .el-select-dropdown__wrap {
  max-height: 70vh;
  text-align: left;
}
</style>
<style scoped>
.empty {
  font-size: 14px;
  padding: 0 20px;
  position: relative;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  color: #606266;
  height: 40px;
  line-height: 40px;
  box-sizing: border-box;
}
.remote-distinct :deep(.el-select__selection) {
  flex-wrap: nowrap;
}
</style>
